/**
 * 
 */
package com.woodbird.legend.instrument;

import java.io.File;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import org.apache.maven.plugin.logging.Log;
import com.woodbird.legend.configuration.Configuration;
import com.woodbird.legend.util.ArrayUtils;

/**
 * @author guangcan
 * @version 2010-2-21
 */
public class DefaultInstrument implements Instrument {

	private Log log;
	private Configuration configuration;

	public DefaultInstrument(Log log,Configuration configuration) {
		this.configuration = configuration;
		this.log = log;
	}

	@Override
	public void instrument(File file) throws InstrumentException {
		if (!file.getName().endsWith("class")) {

		} else {
			try {
				InputStream in = new FileInputStream(file);
				final ClassReader classReader = new ClassReader(in);
				boolean needInstrument = true;
				String[] interfaces = classReader.getInterfaces();
				for (String interfaceName : interfaces) {
					if (interfaceName.equals(Generated.class.getCanonicalName().replaceAll("\\.", "/"))) {
						needInstrument = false;
					}
				}
				if (!needInstrument) {
					log.info(file.getAbsolutePath() + " do not need instrument");
					return;
				}
				FileOutputStream out = new FileOutputStream(file.getAbsolutePath());

				ClassWriter classWriter = new ClassWriter(0);
				ClassVisitor classVisitor = new ClassAdapter(classWriter) {
					@Override
					public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
						String[] newInterfaces = new String[interfaces.length + 1];
						ArrayUtils.copy(interfaces, newInterfaces);
						newInterfaces[interfaces.length] = Generated.class.getCanonicalName().replaceAll("\\.", "/");
						cv.visit(version, access, name, signature, superName, newInterfaces);
					}

					@Override
					public MethodVisitor visitMethod(int access, final String name, String desc,
							String signature, String[] exceptions) {
						MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
						if (!configuration.isNeedInstrument(signature))
							return mv;
						return new MethodAdapter(mv) {
							@Override
							public void visitCode() {
								super.visitCode();
								mv.visitLdcInsn(classReader.getClassName() + "--" + name);
								mv.visitMethodInsn(Opcodes.INVOKESTATIC, 
										"com/woodbird/legend/log/DefaultLogger", "log", "(Ljava/lang/String;)V");
							}

							@Override
							public void visitMaxs(final int maxStack, final int maxLocals) {
								super.visitMaxs(maxStack + 10, maxLocals + 10);
							}
						};
					}
				};
				classReader.accept(classVisitor, 0);
				in.close();
				out.write(classWriter.toByteArray());
				out.close();
				log.info("generated class:" + file.getAbsolutePath());
			} catch (Exception e) {
				throw new InstrumentException(e);
			}
		}

	}

	public static void main(String[] args) throws ClassNotFoundException {
		Instrument instrument = new DefaultInstrument(null,null);
		//Thread.currentThread().getContextClassLoader().loadClass("Test");
		File file = new File("c:/Test.class");
		try {
			instrument.instrument(file);
		} catch (InstrumentException e) {
			e.printStackTrace();
		}
	}

}
